#include <stdlib.h>
#include "Arbre.h"
#include "Symbole.h"
#include "SymboleValue.h"
#include <typeinfo>
using namespace std;

////////////////////////////////////////////////////////////////////////////////
// NoeudSeqInst
////////////////////////////////////////////////////////////////////////////////

NoeudSeqInst::NoeudSeqInst() : tabInst() {
}

////////////////////////////////////////////////////////////////////////////////
int NoeudSeqInst::getValeur() {
  int valeur=0;
  for (unsigned int i=0; i<tabInst.size(); i++)
    valeur = tabInst[i]->getValeur();  // on evalue chaque instruction de la séquence
  return valeur; // par convention, resultat = valeur de la derniere instruction
}

////////////////////////////////////////////////////////////////////////////////
void NoeudSeqInst::afficher(unsigned short indentation) {
  Noeud::afficher(indentation);
  cout << "Noeud - Sequence de " << tabInst.size() << " instruction(s)" << endl;
  for (unsigned int i=0; i<tabInst.size(); i++)
    tabInst[i]->afficher(indentation+1); // on affiche les fils en augmentant l'indentation
}

////////////////////////////////////////////////////////////////////////////////
void NoeudSeqInst::ajouteInstruction(Noeud* instruction) {
  tabInst.push_back(instruction);
}



////////////////////////////////////////////////////////////////////////////////
// NoeudAffectation
////////////////////////////////////////////////////////////////////////////////

NoeudAffectation::NoeudAffectation(Noeud* variable, Noeud* expression) {
  this->variable=variable;
  this->expression=expression;
}

////////////////////////////////////////////////////////////////////////////////
int NoeudAffectation::getValeur() {
  int valeur = expression->getValeur(); // on évalue l'expression
  ((SymboleValue*)variable)->setValeur(valeur); // on affecte la variable
  return valeur; // par convention, une affectation a pour valeur la valeur affectée
}

////////////////////////////////////////////////////////////////////////////////
void NoeudAffectation::afficher(unsigned short indentation) {
  Noeud::afficher(indentation);
  cout << "Noeud - Affectation" << endl;
  variable->afficher(indentation+1);   // on affiche variable et expression
  expression->afficher(indentation+1); // en augmentant l'indentation
}

////////////////////////////////////////////////////////////////////////////////
void NoeudAffectation::translateAda(unsigned short indentation)
{
        Noeud::translateAda(indentation);
        variable->translateAda(indentation+1);
        cout << " := ";
        expression->translateAda(indentation+1);
        cout << endl;
}



////////////////////////////////////////////////////////////////////////////////
// NoeudOperateurBinaire
////////////////////////////////////////////////////////////////////////////////

NoeudOperateurBinaire::NoeudOperateurBinaire ( Symbole operateur,
                                               Noeud* operandeGauche,
                                               Noeud* operandeDroit) {
  this->operateur=operateur;
  this->operandeGauche=operandeGauche;
  this->operandeDroit=operandeDroit;
}

////////////////////////////////////////////////////////////////////////////////
int NoeudOperateurBinaire::getValeur() { 
  int valeur=0;
  int og = 0;
  int od = 0;
  if (operandeDroit ==  NULL)
         og=operandeGauche->getValeur();
  
  else if (operandeGauche == NULL)
         od=operandeDroit->getValeur();
  else
  {
         og=operandeGauche->getValeur();
         od=operandeDroit->getValeur();
  }
  
  if      (this->operateur=="+")   valeur = og+od; 
  else if (this->operateur=="-")   valeur = -od;
  else if (this->operateur=="*")   valeur = og*od;
  else if (this->operateur=="==")  valeur = og == od; 
  else if (this->operateur=="!=")  valeur = og != od; 
  else if (this->operateur=="<")   valeur = og < od; 
  else if (this->operateur=="<=")  valeur = og <= od; 
  else if (this->operateur==">")   valeur = og > od; 
  else if (this->operateur==">=")  valeur = og >= od;
  else if (this->operateur=="non") valeur = !od;
  else if (this->operateur=="et")  valeur = og && od; 
  else if (this->operateur=="ou")  valeur = og || od;
  else if (this->operateur=="++")  valeur = og + 1;
  else if (this->operateur=="--")  valeur = og - 1;
  else if (this->operateur=="+=")  valeur = og + od;
  else if (this->operateur=="-=")  valeur = (og - od);
  else if (this->operateur=="*=")  valeur = og * od;
  else if (this->operateur=="%=")  valeur = og % od;
  else if (this->operateur=="/" || this->operateur=="/=")
      {     if (od!=0)
                    valeur = og/od;
            else 
            {
                    cout << "Erreur pendant l'interpretation : division par zero" << endl;
                    throw Ex();
            }
      }

  return valeur;
}

////////////////////////////////////////////////////////////////////////////////
void NoeudOperateurBinaire::afficher(unsigned short indentation) {
  Noeud::afficher(indentation);
  cout << "Noeud - Operateur Binaire \"" << this->operateur.getChaine() << "\" applique a : " << endl;
  if (operandeGauche == NULL)
  {
      operandeDroit->afficher(indentation+1);     // on affiche fils droit en augmentant l'indentation
  }
  else if (operandeDroit == NULL){
      operandeGauche->afficher(indentation+1);    // on affiche fils gauche en augmentant l'indentation
  }
  else
  {
        operandeGauche->afficher(indentation+1);  // on affiche fils gauche et fils droit
        operandeDroit->afficher(indentation+1);   // en augmentant l'indentation
  }
}

////////////////////////////////////////////////////////////////////////////////
void NoeudOperateurBinaire::translateAda(unsigned short indentation) 
{
        Noeud::translateAda(indentation);
        
        /*if      (operateur->getChaine() == "+")   
                cout << operandeGauche->translateAda(indentation+1) 
                     << " + " << operandeDroit->translateAda(indentation+1); 
        else if (operateur->getChaine() == "-")   
                cout << " - " << operandeDroit->translateAda(indentation+1);
        else if (operateur->getChaine() == "*")   
                cout << operandeGauche->translateAda(indentation+1) 
                     << " * " << operandeDroit->translateAda(indentation+1);
        else if (operateur->getChaine() == "==")  
                cout << operandeGauche->translateAda(indentation+1) 
                     << " = " << operandeDroit->translateAda(indentation+1); 
        else if (operateur->getChaine() == "!=")  
                cout << operandeGauche->translateAda(indentation+1) 
                     << " /= " << operandeDroit->translateAda(indentation+1);  
        else if (operateur->getChaine() == "<")   
                cout << operandeGauche->translateAda(indentation+1) 
                     << " < " << operandeDroit->translateAda(indentation+1);
        else if (operateur->getChaine() == "<=")  
                cout << operandeGauche->translateAda(indentation+1) 
                     << " <= " << operandeDroit->translateAda(indentation+1); 
        else if (operateur->getChaine() == ">")   
                cout << operandeGauche->translateAda(indentation+1) 
                     << " > " << operandeDroit->translateAda(indentation+1);
        else if (operateur->getChaine() == ">=")  
                cout << operandeGauche->translateAda(indentation+1) 
                     << " >= " << operandeDroit->translateAda(indentation+1);
        else if (operateur->getChaine() == "non") 
                cout << " not " << operandeDroit->translateAda(indentation+1);
        else if (operateur->getChaine() == "et")  
                cout << operandeGauche->translateAda(indentation+1) 
                     << " and " << operandeDroit->translateAda(indentation+1); 
        else if (operateur->getChaine() == "ou")  
                cout << operandeGauche->translateAda(indentation+1) 
                     << " or " << operandeDroit->translateAda(indentation+1);
        else if (operateur->getChaine() == "++")
		cout << operandeGauche->translateAda(indentation+1) 
                     << " := " << operandeDroit->translateAda(indentation+1) << " + 1";
	else if (operateur->getChaine() == "--")
		cout << operandeGauche->translateAda(indentation+1) 
                     << " := " << operandeDroit->translateAda(indentation+1) << " - 1";
	else if (operateur->getChaine() == "+=") 
        {
		cout << operandeGauche->translateAda(indentation+1) << " := " 
                     << operandeGauche->translateAda(indentation+1) << " +";
		operandeDroit->translateAda(indentation+1);
	} 
        else if (operateur->getChaine() == "-=") 
        {
		cout << operandeGauche->translateAda(indentation+1) << " := " 
                     << operandeGauche->translateAda(indentation+1) << " -";
		operandeDroit->translateAda(indentation+1);
	} 
        else if (operateur->getChaine() == "*=") 
        {
		cout << operandeGauche->translateAda(indentation+1) << " := " 
                     << operandeGauche->translateAda(indentation+1) << " *";
		operandeDroit->translateAda(indentation+1);
	} 
        else if (operateur->getChaine() == "/=") 
        {
		cout << operandeGauche->translateAda(indentation+1) << " := " 
                     << operandeGauche->translateAda(indentation+1) << " /";
		operandeDroit->translateAda(indentation+1);
	}*/
}

////////////////////////////////////////////////////////////////////////////////
// NoeudInstSi
////////////////////////////////////////////////////////////////////////////////

NoeudInstSi::NoeudInstSi(): tabExpBool(), tabSeqInst() {
}

////////////////////////////////////////////////////////////////////////////////
int NoeudInstSi::getValeur() {
    unsigned int i = 0;
    bool fait = false;
    while (i < tabExpBool.size() && !fait){
        if (tabExpBool[i] != NULL && tabExpBool[i]->getValeur()) 
        {
                tabSeqInst[i]->getValeur(); 
                fait = true;
        }
        
        else if (tabExpBool[i] == NULL) 
        {
                tabSeqInst[i]->getValeur();
        }
        i++;
    }
    return 0;
}

////////////////////////////////////////////////////////////////////////////////
void NoeudInstSi::ajouterExpBool(Noeud* expBool)
{
        tabExpBool.push_back(expBool);
}

////////////////////////////////////////////////////////////////////////////////
void NoeudInstSi::ajouterSeqInst(Noeud* seqInst)
{
        tabSeqInst.push_back(seqInst);
} 

////////////////////////////////////////////////////////////////////////////////
void NoeudInstSi::afficher(unsigned short indentation)
{
        unsigned int i = 0;
        Noeud::afficher(indentation);
        cout << "Noeud - Instruction si :" << endl;
        while( i < tabExpBool.size())
        {
            if (i == 0){
                tabExpBool[i]->afficher(indentation+1);   // on affiche sequence et expression
                tabSeqInst[i]->afficher(indentation+1);   // en augmentant l'indentation
            }
            
            else if (i > 0 && tabExpBool[i] != NULL )
            {
                cout << "Sinon si :" << endl;
                tabExpBool[i]->afficher(indentation+1);   // on affiche sequence et expression
                tabSeqInst[i]->afficher(indentation+1);   // en augmentant l'indentation
            }
            
            else
            {
                cout << "Sinon :" << endl;
                tabSeqInst[i]->afficher(indentation+1);   // on affiche sequence en augmentant l'indentation
            }
            i++;
        }
}
////////////////////////////////////////////////////////////////////////////////
vector<Noeud*> NoeudInstSi::getTabExpBool(){
    return(this->tabExpBool);
}
    
    
////////////////////////////////////////////////////////////////////////////////
vector<Noeud*> NoeudInstSi::getTabSeqInst(){
    return(this->tabSeqInst);
}

////////////////////////////////////////////////////////////////////////////////
void NoeudInstSi::translateAda(unsigned short indentation)
{
        Noeud::translateAda(indentation);
}



////////////////////////////////////////////////////////////////////////////////
// NoeudInstTq
////////////////////////////////////////////////////////////////////////////////

NoeudInstTq::NoeudInstTq(Noeud* expBool, Noeud* seqInst) {
  this->expBool=expBool;
  this->seqInst=seqInst;
}

////////////////////////////////////////////////////////////////////////////////
int NoeudInstTq::getValeur() {
  while(expBool->getValeur())
  {
        seqInst->getValeur();
  }
  return 0;                                  
}

////////////////////////////////////////////////////////////////////////////////
void NoeudInstTq::afficher(unsigned short indentation)
{
        Noeud::afficher(indentation);
        cout << "Noeud - Tant que : " << endl;
        expBool->afficher(indentation+1);
        cout << "Noeud - Faire : " << endl;
        seqInst->afficher(indentation+1);
}

////////////////////////////////////////////////////////////////////////////////
Noeud* NoeudInstTq::getExpBool(){
    return(this->expBool);
}
    
////////////////////////////////////////////////////////////////////////////////
Noeud* NoeudInstTq::getSeqInst(){
    return(this->seqInst);
}

////////////////////////////////////////////////////////////////////////////////
// NoeudInstRepeter
////////////////////////////////////////////////////////////////////////////////

NoeudInstRepeter::NoeudInstRepeter(Noeud* expBool, Noeud* seqInst):NoeudInstTq(expBool,seqInst) {}

////////////////////////////////////////////////////////////////////////////////
int NoeudInstRepeter::getValeur() {
    Noeud* expBool = this->getExpBool();
    Noeud* seqInst = this->getSeqInst();
    seqInst->getValeur();
    while(!expBool->getValeur()){
        seqInst->getValeur();
  }      
  return 0;                                  
}

////////////////////////////////////////////////////////////////////////////////
void NoeudInstRepeter::afficher(unsigned short indentation)
{
    Noeud* expBool = this->getExpBool();
    Noeud* seqInst = this->getSeqInst();
    Noeud::afficher(indentation);
    cout << "Noeud - Repeter : " << endl;
    seqInst->afficher(indentation+1);
    cout << "Noeud - Jusqua : " << endl;
    expBool->afficher(indentation+1);
}



////////////////////////////////////////////////////////////////////////////////
// NoeudInstPour
////////////////////////////////////////////////////////////////////////////////

NoeudInstPour::NoeudInstPour(Noeud* affectation1, Noeud* expBool, Noeud* affectation2, Noeud* seqInst) {
    this->affectation1=affectation1;
    this->expBool=expBool;
    this->affectation2=affectation2;
    this->seqInst=seqInst;
}

////////////////////////////////////////////////////////////////////////////////
int NoeudInstPour::getValeur() {
    for (this->affectation1->getValeur() ; this->expBool->getValeur() ; this->affectation2->getValeur())
        this->seqInst->getValeur();
    return 0;
}

////////////////////////////////////////////////////////////////////////////////
void NoeudInstPour::afficher(unsigned short indentation)
{
        Noeud::afficher(indentation);
        cout << "Noeud - Pour : " << endl;
        cout << "(";
        affectation1->afficher(indentation+1);
        cout << " ; ";
        expBool->afficher(indentation+1);
        cout << " ; ";
        affectation2->afficher(indentation+1);
        cout << ")";
        seqInst->afficher(indentation+1);
}

////////////////////////////////////////////////////////////////////////////////
void NoeudInstPour::translateAda(unsigned short indentation)
{
        Noeud::translateAda(indentation);
        /*cout << affectation1-;
        affectation1->afficher(indentation+1);
        cout << "for";*/
}



////////////////////////////////////////////////////////////////////////////////
// NoeudInstLire
////////////////////////////////////////////////////////////////////////////////
NoeudInstLire::NoeudInstLire(Noeud* variable) {
        this->variable=variable;
}

////////////////////////////////////////////////////////////////////////////////
int NoeudInstLire::getValeur() {
        int var;
        cin >> var;                                // on évalue la variable
        ((SymboleValue*)variable)->setValeur(var); // on affecte la variable
        return 0;                   
}

////////////////////////////////////////////////////////////////////////////////
void NoeudInstLire::afficher(unsigned short indentation)
{
        Noeud::afficher(indentation);
        cout << "Noeud - Lire : " << endl;
        variable->afficher(indentation+1);
}

////////////////////////////////////////////////////////////////////////////////
void NoeudInstLire::translateAda(unsigned short indentation)
{
        Noeud::translateAda(indentation);
        cout << "Lire (";
        variable->translateAda(indentation+1);
        cout << ")";
        cout << endl;
}



////////////////////////////////////////////////////////////////////////////////
// NoeudInstEcrire
////////////////////////////////////////////////////////////////////////////////

NoeudInstEcrire::NoeudInstEcrire(Noeud* expression) {
    this->expression=expression;
}

////////////////////////////////////////////////////////////////////////////////
int NoeudInstEcrire::getValeur() {
    if (typeid (* expression) == typeid (SymboleValue) && (*(SymboleValue *)expression=="<CHAINE>"))
            cout << ((SymboleValue *)expression)->getChaine().substr(1,((SymboleValue *)expression)->getChaine().size()-2);
    else
            cout << expression->getValeur();
    return 0;
}

////////////////////////////////////////////////////////////////////////////////
void NoeudInstEcrire::afficher(unsigned short indentation)
{
        Noeud::afficher(indentation);
        cout << "Noeud - Ecrire : " << endl;
        expression->afficher(indentation+1);   
}

////////////////////////////////////////////////////////////////////////////////
void NoeudInstEcrire::translateAda(unsigned short indentation)
{ 
      Noeud::translateAda(indentation);
      cout << "Ecrire (";
      expression->translateAda(indentation+1);
      cout << ")";
      cout << endl;
}



////////////////////////////////////////////////////////////////////////////////
// NoeudInstSelon
////////////////////////////////////////////////////////////////////////////////

NoeudInstSelon::NoeudInstSelon(Noeud* variable) :NoeudInstSi() {
                this->variable = variable;
}

////////////////////////////////////////////////////////////////////////////////

void NoeudInstSelon::afficher(unsigned short indentation) {
        Noeud::afficher(indentation);
        unsigned int i =0;
        cout << "Noeud - Selon : appliqué à : "<<endl;
        variable->afficher(indentation+1);
        vector<Noeud*> tabExpBool = this->getTabExpBool();
        vector<Noeud*> tabSeqInst = this->getTabSeqInst();
        while( i < tabExpBool.size())
        {
            if (i == 0){
                tabExpBool[i]->afficher(indentation+1);   // on affiche sequence et expression
                tabSeqInst[i]->afficher(indentation+1);   // en augmentant l'indentation
            }
            
            else if (i > 0 && tabExpBool[i] != NULL )
            {
                cout << "Cas :" << endl;
                tabExpBool[i]->afficher(indentation+1);   // on affiche sequence et expression
                tabSeqInst[i]->afficher(indentation+1);   // en augmentant l'indentation
            }
            
            else
            {
                cout << "Defaut :" << endl;
                tabSeqInst[i]->afficher(indentation+1);   // on affiche sequence en augmentant l'indentation
            }
            i++;
        }
}
////////////////////////////////////////////////////////////////////////////////
int NoeudInstSelon::getValeur(){
        vector<Noeud*> tabExpBool = this->getTabExpBool();
        vector<Noeud*> tabSeqInst = this->getTabSeqInst();
            unsigned int i = 0;
    bool fait = false;
    while (i < tabExpBool.size() && !fait){
        if (tabExpBool[i] != NULL && tabExpBool[i]->getValeur()== variable->getValeur())
        {
                tabSeqInst[i]->getValeur(); 
                fait = true;
        }
        
        else if (tabExpBool[i] == NULL) 
        {
                tabSeqInst[i]->getValeur();
        }
        i++;
    }
    return 0;
}

////////////////////////////////////////////////////////////////////////////////
void NoeudInstSelon::translateAda(unsigned short indentation)
{
      Noeud::translateAda(indentation);

}
